package com.rpa.server.servicerpaserver.crawler.operate;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.rpa.secure.servicerpasecure.common.constant.MessageConstant;
import com.rpa.secure.servicerpasecure.common.constant.ParameterConstant;
import com.rpa.secure.servicerpasecure.common.constant.UrlConstant;
import com.rpa.secure.servicerpasecure.common.exception.TaskException;
import com.rpa.secure.servicerpasecure.common.util.CurrentTeacherInfoUtil;
import com.rpa.server.servicerpaserver.crawler.entity.*;
import com.rpa.server.servicerpaserver.crawler.entity.dto.TestDto;
import com.rpa.server.servicerpaserver.crawler.entity.dto.WorkDto;
import com.rpa.server.servicerpaserver.crawler.service.ScoreService;
import com.rpa.server.servicerpaserver.crawler.service.TestService;
import com.rpa.server.servicerpaserver.crawler.service.UserService;
import com.rpa.server.servicerpaserver.util.CookieSerialize;
import com.rpa.server.servicerpaserver.util.UrlUtil;
import com.rpa.server.servicerpaserver.util.WebDriverUtil;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.zip.GZIPInputStream;

@Slf4j
@Component
public class TestOperate {
    @Autowired
    private CookieSerialize cookieSerialize;
    @Autowired
    private UserService uerService;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private TestService testService;
    @Autowired
    private  RestTemplate restTemplate;
    @Autowired
    private ScoreService scoreService;

    @Async("testTaskExecutor")
    public CompletableFuture<Boolean> getTest(TestDto testDto) {
        log.info("获取考试信息");

        // 校验用户是否存在
        User user = uerService.getById(testDto.getAccountId());
        if (user == null) {
            throw new TaskException(MessageConstant.NO_USER);
        }
        Map stringObjectMap = BeanUtil.beanToMap(testDto);

        // 调用异步方法获取班级id
        CompletableFuture<List<Map>> classIdsFuture = this.getTestList(stringObjectMap);

        // 异步任务处理，等待结果
        classIdsFuture.thenAccept(testId -> {
            try {
                if (CollUtil.isEmpty(testId)) {
                    throw new TaskException(MessageConstant.DATA_NULL);
                }
                testId.forEach(classId -> {
                    handleSingleClass(classId);
                });
            } catch (TaskException e) {
                // 记录日志或采取其他措施
                log.error("处理班级信息时发生错误: {}", e.getMessage(), e);
            }
        }).exceptionally(throwable -> {
            // 处理所有未捕获的异常
            log.error("异步任务中发生未知错误: {}", throwable.getMessage(), throwable);
            return null; // 或者返回一个默认值
        });


        return CompletableFuture.completedFuture(true); // 这里可以返回处理的状态，如果有需要
    }

    @Transactional
    public void handleSingleClass(Map objectObjectHashMap) {
        List<Test> classNameList = this.getTestDetail(objectObjectHashMap);
        if (CollUtil.isEmpty(classNameList)) {
            log.error(MapUtil.getStr(objectObjectHashMap, "testId")+MessageConstant.DATA_NULL);
        }
        // 批量更新班级信息
        RLock lock = redissonClient.getLock(objectObjectHashMap.get("testId").toString());
        try{
            if (lock.tryLock(10, 30, TimeUnit.SECONDS)) {
                this.deleteTest(objectObjectHashMap);
                testService.saveBatch(classNameList);
                saveTestScore(objectObjectHashMap,classNameList);
            }else{
                log.error("获取锁失败");
            }
        }catch (InterruptedException e) {
            log.error("获取锁失败", e);
        }finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    private void saveTestScore(Map objectObjectHashMap, List<Test> classNameList) {
        String str = MapUtil.getStr(objectObjectHashMap, "testName", "");
        if (!str.contains("期末") && !str.contains("期中")) {
            return;
        }

        LinkedList<Score> scores = new LinkedList<>();
        int scoreType = str.contains("期末") ? 1 : 2;

        for (Test test : classNameList) {
            Score score = buildScore(test, scoreType);
            scores.add(score);
        }

        LambdaUpdateWrapper<Score> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        lambdaUpdateWrapper.eq(Score::getAccountId, MapUtil.getStr(objectObjectHashMap, "accountId", ""))
                .eq(Score::getCourseId, MapUtil.getStr(objectObjectHashMap, "courseId", ""))
                .eq(Score::getClazzId, MapUtil.getStr(objectObjectHashMap, "classId", ""))
                .eq(Score::getScoreType, scoreType);

        scoreService.remove(lambdaUpdateWrapper);
        scoreService.saveBatch(scores);
    }

    private Score buildScore(Test test, int scoreType) {
        return Score.builder()
                .accountId(test.getAccountId())
                .courseId(test.getCourseId())
                .clazzId(test.getClazzId())
                .scoreNumber(test.getScore())
                .scoreType(scoreType)
                .userName(test.getUserName())
                .isStart(0)
                .studentNumber(test.getStudentNumber())
                .build();
    }


    private Boolean deleteTest(Map objectObjectHashMap) {
        LambdaUpdateWrapper<Test> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        lambdaUpdateWrapper.eq(Test::getTestId,MapUtil.getStr(objectObjectHashMap, "testId", ""));
        return testService.remove(lambdaUpdateWrapper);
    }

    @Async("testListTaskExecutor")
    public CompletableFuture<List<Map>> getTestList(Map map) {
        ChromeOptions options = new ChromeOptions();
        WebDriverUtil.setupWebDriver(options);
        ChromeDriver chromeDriver = null;
        List<Map> arrayList = new ArrayList<>();
        String accountId = MapUtil.getStr(map, "accountId", "");
        String courseId = MapUtil.getStr(map, "courseId", "");
        String classId = MapUtil.getStr(map, "classId", "");
        try {
            chromeDriver = new ChromeDriver(options);
            cookieSerialize.goFZF1(accountId, chromeDriver);
            chromeDriver.get(StrUtil.format(UrlConstant.TEST_LIST, courseId,classId));
            cookieSerialize.delAfterSaveCookies(accountId, chromeDriver.manage().getCookies());
            WebDriverWait webDriverWait = new WebDriverWait(chromeDriver, Duration.ofSeconds(5));
            webDriverWait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("//html/body/div[@class='box']")));
            List<WebElement> elements = chromeDriver.findElements(By.xpath("//*[@id=\"fanyaTask\"]/div[2]/div[2]/ul/li"));
            for (WebElement element : elements){
                Map map1 = new HashMap();
                try {
                    map1.putAll(map);
                    WebElement testName = element.findElement(By.xpath("./div[1]/div[1]/div[1]/h2"));
                    map1.put("testName",testName.getText());
                    WebElement linkA = element.findElement(By.xpath("./div[4]/a"));
                    String href = linkA.getAttribute("href");
                    Map<String, String> queryParams = UrlUtil.getQueryParams(href);
                    map1.put("cpi",queryParams.get("cpi"));
                    map1.put("paperId",queryParams.get("paperId"));
                    map1.put("testId",queryParams.get("id"));
                    arrayList.add(map1);
                }catch (Exception e){
                    log.error("获取考试信息失败", e);
                }
            }
        } catch (Exception e) {
            log.error("获取考试信息失败", e);
            throw new TaskException(MessageConstant.GET_SIGN_ERROR);
        } finally {
            if (chromeDriver != null) {
                chromeDriver.quit();
            }
        }
        return CompletableFuture.completedFuture(arrayList);
    }

    @Async("testDetailTaskExecutor")
    public List<Test> getTestDetail(Map map) {
        ChromeOptions options = new ChromeOptions();
        WebDriverUtil.setupWebDriver(options);
        ChromeDriver chromeDriver = null;
        List<Test> arrayList = new ArrayList<>();
        String accountId = MapUtil.getStr(map, "accountId", "");
        MultiValueMap<String, String> requestParamsTrue = this.setPrequestParams(map, ParameterConstant.ONE);
        MultiValueMap<String, String> requestParamsFalse = this.setPrequestParams(map, ParameterConstant.ZERO);
        HttpHeaders headers =this.setHeader(accountId);
        HttpEntity<MultiValueMap<String, String>> entityTrue = new HttpEntity<>(requestParamsTrue, headers);
        HttpEntity<MultiValueMap<String, String>> entityFalse = new HttpEntity<>(requestParamsFalse, headers);

        try {
            // 发送 POST 请求，并使用 String 接收响应
            ResponseEntity<String> responseTrue = restTemplate.exchange(UrlConstant.TEST_DETAIL, HttpMethod.POST, entityTrue, String.class);
            ResponseEntity<String> responseFalse = restTemplate.exchange(UrlConstant.TEST_DETAIL, HttpMethod.POST, entityFalse, String.class);

            // 获取压缩后的响应体
            String compressedBodyTrue = responseTrue.getBody();
            String compressedBodyFalse = responseFalse.getBody();

            // 手动解压缩
            //String htmlTrue = UrlUtil.decompressGzip(compressedBodyTrue);
           //String htmlFalse = UrlUtil.decompressGzip(compressedBodyFalse);

            List<Test> testDetailElementTrue = this.getTestDetailElement(compressedBodyTrue, map, true);
            List<Test> testDetailElementFalse = this.getTestDetailElement(compressedBodyFalse, map, false);
            arrayList.addAll(testDetailElementTrue);
            arrayList.addAll(testDetailElementFalse);
        }catch (Exception e) {
            log.error("获取考试详细信息失败", e);
        }finally {
            if (chromeDriver != null) {
                chromeDriver.quit();
            }
        }
        return arrayList;
    }

    public List<Test> getTestDetailElement(String html, Map map,boolean isOver) {
        Document document = Jsoup.parse(html);
        List<Test> objects = new LinkedList<>();
        // 提取数据（示例：提取学生姓名和分数）
        Elements studentElements = document.select("ul.dataBody_td");
        for (Element studentElement : studentElements) {
            try{
                String name = studentElement.select("div.py_name").text().trim();
                String studentNumber = "";
                String receiveTime="";
                String completeTime ="";
                String consumingTime ="";
                String score ="";
                if (isOver){
                    receiveTime = studentElement.select("span.receiveTime").text().trim();
                    studentNumber =studentElement.select("li.taskBody_con.wid_bf_10").attr("title".trim());
                    completeTime =studentElement.child(4).text().trim();
                    consumingTime=studentElement.child(5).text().trim();
                    score = StrUtil.isBlank(studentElement.select("input.scoreInput").attr("value")) ? "0" : studentElement.select("input.scoreInput").attr("value");
                }else {
                    studentNumber =studentElement.child(2).text().trim();
                    score = StrUtil.isBlank(studentElement.select("input.scoreInput").attr("value")) ? "0" :studentElement.child(5).text().trim();
                    completeTime =studentElement.child(4).text().trim();
                    consumingTime=studentElement.child(4).text().trim();
                    receiveTime=studentElement.child(4).text().trim();
                }

                Test build = Test.builder().testId(MapUtil.getStr(map, "testId", ""))
                        .accountId(MapUtil.getStr(map, "accountId", ""))
                        .courseId(MapUtil.getStr(map, "courseId", ""))
                        .clazzId(MapUtil.getStr(map, "classId", ""))
                        .userName(name)
                        .studentNumber(studentNumber)
                        .receiveTime(receiveTime)
                        .score(Double.parseDouble(score))
                        .completeTime(completeTime)
                        .consumingTime(consumingTime)
                        .testName(MapUtil.getStr(map, "testName", ""))
                        .build();
                objects.add(build);
            }catch (Exception e){
                log.error("获取考试信息失败", e);
            }
        }
        return objects;
    }
    public HttpHeaders setHeader(String accountId){
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        headers.set("Accept", ParameterConstant.ACCEPT);
        headers.set("Accept-Encoding", ParameterConstant.ACCEPT_ENCODING);
        headers.set("Accept-Language",ParameterConstant.ACCEPT_LANGUAGE);
        headers.set("Connection",ParameterConstant.CONNECTION );
        headers.set("Cookie", cookieSerialize.getCookieString(accountId));
       // headers.set("Cookie", "k8s=1738574986.193.15292.108248; route=f537d772be8122bff9ae56a564b98ff6; thirdRegist=0; fid=43880; _uid=236204222; uf=d9387224d3a6095b23876159345cca6f450e9d5c810a33c214f3b535708c77cd7913f4c8679b6bb68f654d2de6df6738d110c105546a283d3b0b46d7f25da7ee6e8602680cb77da400a9dbfa51b1bc3e260b22ede7461b6b70eda7aba3b899fe3f6432e6ac0fb457aa2ebad65cd196bb; _d=1738675753079; UID=236204222; vc2=ECAE2C38497984A53367EBCE12A315F5; vc3=fWniIiYu1I2IkqeeugrxwoZz7jCphEp1bLstSN4KpDYzs82ePs2wzh%2FSUQhxWHRRiyY%2BNFPIewWYGq5FhT%2FR2qpOwWovNjYeAWDTvdHBuOqwE8nU5%2BZEAUZfGNGFp3ttWNURbpBv%2Fu5sJViRgP6DjeSCVIoAsstvHG2uA6ev7V4%3D5322ecd8c7366eecfdd91478553bd261; cx_p_token=bd2ec945374c1a3f0a15ea53a30612ba; p_auth_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiIyMzYyMDQyMjIiLCJsb2dpblRpbWUiOjE3Mzg2NzU3NTMwODEsImV4cCI6MTczOTI4MDU1M30.7kM15pCTr0q3KAzFwJw3QYtkgKbYrawNZvxoHenVyrs; xxtenc=8bd470d0da0c9aed67ff7df5612f2252; DSSTASH_LOG=C_38-UN_1225-US_236204222-T_1738675753081; tl=1; jrose=2C88B607BED92A9AD2B34BBDA8BE8DB2.mooc2-2161198184-bx07z");
        headers.set("Host",ParameterConstant.HOST_TEST);
        headers.set("Origin",ParameterConstant.ORIGIN_TEST);
        return headers;
    }

    public MultiValueMap<String, String> setPrequestParams(Map map, String state){
        String testId = MapUtil.getStr(map, "testId", "");
        String courseId = MapUtil.getStr(map, "courseId", "");
        String classId = MapUtil.getStr(map, "classId", "");
        String cpi = MapUtil.getStr(map, "cpi", "");


        MultiValueMap<String, String> requestParams = new LinkedMultiValueMap<>();
        requestParams.add("courseid", courseId);
        requestParams.add("clazzid", classId);
        requestParams.add("cpi", cpi);
        requestParams.add("id", testId);
        requestParams.add("state", state);
        requestParams.add("size", ParameterConstant.TWENTY_THOUSAND);
        requestParams.add("pages", ParameterConstant.ONE);
        return requestParams;
    }
}
